package org.example.system.controller;

import com.alibaba.fastjson2.JSON;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.example.utils.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static org.apache.commons.io.FileUtils.deleteDirectory;

/**
 * 图片服务器控制器
 */
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
    @Value("${system.baseMediaUrl}")
    private String BASE_MEDIA_URL; //存储路径

    public static String readProcessOutput(Process process) throws IOException {
        StringBuilder output = new StringBuilder();
        try (InputStream inputStream = process.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line);
            }
        }
        return output.toString();
    }

    /**
     * 上传头像
     * @return
     */
    @PostMapping("/upload/avatar")
    public R uploadAvatar(@RequestParam("file")  MultipartFile fileUpload) {
        String fileName = fileUpload.getOriginalFilename();
        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //重新生成文件名
        fileName = UUID.randomUUID()+suffixName;
        //指定本地文件夹存储图片，写到需要保存的目录下
        String filePath = BASE_MEDIA_URL + "imgs/avatar/";
        File file =new File(filePath+fileName);
        //文件目录不存在则递归创建目录
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                log.error("创建文件夹异常");
                return R.fail("上传失败");
            }
        }
        try {
            fileUpload.transferTo(file);
            log.info("--------- FileController | upload | return : {}",JSON.toJSON(fileName));
            //返回提示信息
            return R.ok(200,"上传成功",fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("上传失败");
        }
    }
    /**
     * 上传封面
     * @return
     */
    @PostMapping("/upload/cover")
    public R uploadCover(@RequestParam("file")  MultipartFile fileUpload,@RequestParam("userId") Long userId) {
        String fileName = fileUpload.getOriginalFilename();
        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //重新生成文件名
        fileName = UUID.randomUUID()+suffixName;
        //指定本地文件夹存储图片，写到需要保存的目录下
        String filePath = BASE_MEDIA_URL + "imgs/cover/" + userId.toString() + "/";
        File file =new File(filePath+fileName);
        //文件目录不存在则递归创建目录
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                log.error("创建文件夹异常");
                return R.fail("上传失败");
            }
        }
        try {
            fileUpload.transferTo(file);
            log.info("--------- FileController | upload | return : {}",JSON.toJSON("/" + userId + "/" +fileName));
            //返回提示信息
            return R.ok(200,"上传成功" ,"/" + userId.toString() + "/" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("上传失败");
        }
    }
    /**
     * 上传小说
     * @return
     */
    @PostMapping("/upload/novel")
    public R uploadNovel(@RequestParam("file")  MultipartFile fileUpload,@RequestParam("userId") Long userId) {
        // 检查文件大小（500KB）
        if (fileUpload.getSize() > 500 * 1024) {
            return R.fail("文件大小不能超过 500KB");
        }
        String fileName = fileUpload.getOriginalFilename();
        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //重新生成文件名
        fileName = UUID.randomUUID()+suffixName;
        //指定本地文件夹存储图片，写到需要保存的目录下
        String filePath = BASE_MEDIA_URL + "novel/" + userId.toString() + "/";
        File file =new File(filePath+fileName);
        //文件目录不存在则递归创建目录
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                log.error("创建文件夹异常");
                return R.fail("上传失败");
            }
        }
        try {
            fileUpload.transferTo(file);
            log.info("--------- FileController | upload | return : {}",JSON.toJSON("/" + userId + "/" +fileName));
            //返回提示信息
            return R.ok(200,"上传成功" ,(Object) "/" + userId.toString() + "/" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("上传失败");
        }
    }
    /**
     * 上传漫画
     * @return
     */
    @PostMapping("/upload/comic")
    public R uploadComic(@RequestParam("file") MultipartFile fileUpload,@RequestParam("userId") Long userId) {
        // 检查文件大小（100MB）
        if (fileUpload.getSize() > 100 * 1024 * 1024) {
            return R.fail("文件大小不能超过 100MB");
        }
        String fileName = fileUpload.getOriginalFilename();
        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //重新生成文件名
        fileName = UUID.randomUUID()+suffixName;
        //指定本地文件夹存储图片，写到需要保存的目录下
        String filePath = BASE_MEDIA_URL + "comic/" + userId.toString() + "/";
        File file =new File(filePath+fileName);
        //文件目录不存在则递归创建目录
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                log.error("创建文件夹异常");
                return R.fail("上传失败");
            }
        }
        try {
            fileUpload.transferTo(file);
            if (!extractImages(filePath+fileName)){
                return R.fail("上传失败");
            }

            log.info("--------- FileController | upload | return : {}",JSON.toJSON("/" + userId + "/" + fileName.substring(0, fileName.lastIndexOf('.')) + ".epub"));
            //返回提示信息
            return R.ok(200,"上传成功" ,(Object) "/" + userId.toString() + "/" + fileName.substring(0, fileName.lastIndexOf('.')) + ".epub");
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("上传失败");
        }
    }
    /**
     * 上传视频
     * @return
     */
    @PostMapping("/upload/video")
    public R uploadVideo(@RequestParam("file")  MultipartFile fileUpload,@RequestParam("userId") Long userId) {
        // 检查文件大小（1GB）
        if (fileUpload.getSize() > 1 * 1024 * 1024 * 1024) {
            return R.fail("文件大小不能超过 1GB");
        }
        String fileName = fileUpload.getOriginalFilename();
        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //重新生成文件名
        fileName = UUID.randomUUID()+suffixName;
        //指定本地文件夹存储图片，写到需要保存的目录下
        String filePath = BASE_MEDIA_URL + "video/" + userId.toString() + "/";
        File file =new File(filePath+"input.mp4");
        //文件目录不存在则递归创建目录
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                log.error("创建文件夹异常");
                return R.fail("上传失败");
            }
        }
        try {
            fileUpload.transferTo(file);
            String fullName = filePath + fileName;
            if(!isVideoValid(filePath+"input.mp4")){
                convertToH264(filePath+"input.mp4", fullName);
            }else{
                Files.move(Paths.get(filePath+"input.mp4"),Paths.get(fullName),StandardCopyOption.REPLACE_EXISTING);
            }
            log.info("--------- FileController | upload | return : {}", JSON.toJSON("/" + userId + "/" +fileName));
            //返回提示信息
            return R.ok(200,"上传成功" ,(Object) "/" + userId.toString() + "/" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("上传失败");
        }
    }

    /**
     * 获取所有文件列表
     * @return
     * @throws IOException
     */
    @GetMapping("/list")
    public R listAllFiles(@RequestParam("mediaPath") String mediaPath) throws IOException {
        String directory = BASE_MEDIA_URL + mediaPath.substring(0, mediaPath.lastIndexOf('.')) + "/";
        // 使用Java NIO的Files类遍历目录
        List<String> fileNames = Files.walk(Paths.get(directory))
                .filter(Files::isRegularFile)
                .map(Path::getFileName)
                .map(Path::toString)
                .collect(Collectors.toList());
        return R.ok(fileNames);
    }

    /**
     * mobi图片提取
     * @param inputPath
     * @return
     */
    public static boolean extractImages(String inputPath) throws IOException, InterruptedException{
        String epubPath = inputPath.substring(0, inputPath.lastIndexOf('.')) + ".epub";
        //检测是否是mobi文件,是则转换为epub
        if(inputPath.toLowerCase().endsWith(".mobi")){
            String calibreCommand = "ebook-convert";
            ProcessBuilder pb = new ProcessBuilder(calibreCommand, inputPath, epubPath);
            pb.redirectErrorStream(true); // 合并错误流和输出流
            System.out.println("执行命令: " + String.join(" ", pb.command()));
            Process process = pb.start();
            // 打印进程输出
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println("[Calibre] " + line);
                }
            }

            // 设置超时并等待
            if (!process.waitFor(5, TimeUnit.MINUTES)) {
                process.destroy();
                throw new IOException("转换超时");
            }

            if (process.exitValue() != 0) {
                throw new IOException("转换失败，退出码: " + process.exitValue());
            }
        }
        //解压图片
        try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(epubPath))) {
            String outputDir = inputPath.substring(0, inputPath.lastIndexOf('.')) + "/";
            // 创建输出目录
            new File(outputDir).mkdirs();
            // 遍历 ZIP 条目
            ZipEntry entry;
            while ((entry = zipIn.getNextEntry()) != null) {
                String entryName = entry.getName();
                // 提取图片（通常位于 OEBPS/Images 或 image/ 目录）
                if (entryName.toLowerCase().endsWith(".jpg")
                        || entryName.toLowerCase().endsWith(".png")
                        || entryName.toLowerCase().endsWith(".jpeg")) {
                    // 提取文件名（去除路径）
                    String fileName = new File(entryName).getName();
                    Path outputPath = Paths.get(outputDir, fileName);
                    // 写入图片文件
                    Files.copy(zipIn, outputPath, StandardCopyOption.REPLACE_EXISTING);
                    System.out.println("提取图片: " + fileName);
                }
                zipIn.closeEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        try {
            Path path = Paths.get(inputPath);
            if (Files.exists(path)) {
                Files.delete(path);
                System.out.println("已删除: " + inputPath);
            }
        } catch (IOException e) {
            System.err.println("删除文件失败: " + inputPath);
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 检查视频是否符合 H.264 + AAC 要求
     * @param videoPath 视频文件路径
     * @return true=符合 | false=不符合或解析失败
     */
    public static boolean isVideoValid(String videoPath) {
        try {
            // 1. 执行 ffprobe 命令获取视频元数据（JSON格式）
            String[] cmd = {
                    "ffprobe",
                    "-v", "error",                  // 只显示错误
                    "-select_streams", "v:0",       // 检查视频流
                    "-show_entries", "stream=codec_name,pix_fmt",
                    "-of", "json",                  // 输出JSON
                    videoPath
            };
            Process process = new ProcessBuilder(cmd).start();
            String jsonOutput = readProcessOutput(process);

            // 2. 解析JSON，检查视频流
            JsonObject probeData = JsonParser.parseString(jsonOutput).getAsJsonObject();
            JsonArray streams = probeData.getAsJsonArray("streams");
            if (streams.size() == 0) {
                System.err.println("错误：未检测到视频流");
                return false;
            }

            JsonObject videoStream = streams.get(0).getAsJsonObject();
            String videoCodec = videoStream.get("codec_name").getAsString();
            String pixelFormat = videoStream.get("pix_fmt").getAsString();

            boolean isVideoValid = videoCodec.equals("h264") && pixelFormat.equals("yuv420p");

            // 3. 检查音频流（AAC）
            boolean isAudioValid = checkAudioCodec(videoPath);

            return isVideoValid && isAudioValid;
        } catch (IOException | InterruptedException e) {
            System.err.println("解析失败: " + e.getMessage());
            return false;
        }
    }
    /**
     * 检查音频是否为 AAC
     */
    private static boolean checkAudioCodec(String videoPath) throws IOException, InterruptedException {
        String[] cmd = {
                "ffprobe",
                "-v", "error",
                "-select_streams", "a:0",       // 检查音频流
                "-show_entries", "stream=codec_name",
                "-of", "json",
                videoPath
        };
        Process process = new ProcessBuilder(cmd).start();
        String jsonOutput = readProcessOutput(process);

        JsonObject probeData = JsonParser.parseString(jsonOutput).getAsJsonObject();
        JsonArray streams = probeData.getAsJsonArray("streams");
        if (streams.size() == 0) {
            System.err.println("警告：未检测到音频流");
            return false; // 无音频也算不符合
        }

        String audioCodec = streams.get(0).getAsJsonObject().get("codec_name").getAsString();
        return audioCodec.equals("aac");
    }
    /**
     * 视频转码
     * @param inputPath
     * @param outputPath
     * @throws IOException
     */
    public static void convertToH264(String inputPath, String outputPath) throws IOException, InterruptedException {
        // 1. 检查并创建输出目录
        Path outputDir = Paths.get(outputPath).getParent();
        if (!Files.exists(outputDir)) {
            Files.createDirectories(outputDir);
        }

        // 2. 使用临时文件（避免输入=输出问题）
        String tempPath = outputPath.replace(".mp4", "_temp.mp4");

        // 3. FFmpeg 命令（强制覆盖 + 标准编码参数）
        String[] cmd = {
                "ffmpeg",
                "-y",                     // 覆盖临时文件
                "-i", inputPath,          // 输入
                "-c:v", "libx264",        // H.264 编码
                "-pix_fmt", "yuv420p",    // 兼容像素格式
                "-c:a", "aac",            // AAC 音频
                tempPath                  // 输出到临时文件
        };

        // 4. 执行并监控 FFmpeg 进程
        Process process = new ProcessBuilder(cmd)
                .redirectErrorStream(true)
                .start();

        // 5. 打印 FFmpeg 日志（防止缓冲区阻塞）
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println("[FFmpeg] " + line);
            }
        }

        // 6. 检查是否成功
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            throw new IOException("FFmpeg 失败，错误码: " + exitCode);
        }

        // 7. 删除旧文件（可选），重命名临时文件
        Files.deleteIfExists(Paths.get(outputPath));
        Files.move(
                Paths.get(tempPath),
                Paths.get(outputPath),
                StandardCopyOption.REPLACE_EXISTING
        );
        Files.deleteIfExists(Paths.get(inputPath));
        System.out.println("转码成功！输出路径: " + outputPath);
    }

    /**
     * 删除
     * @return
     */
    @PostMapping("/remove")
    public R remove(@RequestBody Map<String, String> body) {
        String url = body.get("url");
        String filePath = BASE_MEDIA_URL + url;
        Path path = Paths.get(filePath);
        File directory = new File(filePath.substring(0, filePath.lastIndexOf('.')) + "/");
        try {
            Files.delete(path);
            if (directory.exists()) {
                File[] files = directory.listFiles();
                if (files != null) {
                    for (File file : files) {
                        if (file.isDirectory()) {
                            deleteDirectory(file);
                        } else {
                            file.delete();
                        }
                    }
                }
                directory.delete();
            }
            System.out.println("文件删除成功！");
        } catch (IOException e) {
            System.err.println("文件删除失败: " + e.getMessage());
            return R.ok("文件删除失败！");
        }
        return R.ok("文件删除成功！");
    }
}